home *** CD-ROM | disk | FTP | other *** search
/ MacTech 1 to 12 / MacTech-vol-1-12.toast / Source / MacTech® Magazine / Volume 05 - 1989 / 05.06 Jun 89 / LOC Source / Test Source / PopMenuCDEF.p < prev    next >
Encoding:
Text File  |  1988-09-28  |  26.4 KB  |  1,199 lines  |  [TEXT/MPS ]

  1. (*****************************************************
  2. PopMenuCDEF.p
  3.    This file contains the Pascal source code for the
  4.    routines needed to implement the pop-up menu
  5.    button CDEF described (in passing) in Inside Mac,
  6.    v5, p242.
  7. *****************************************************)
  8.  
  9. UNIT PopMenuCDEF;
  10.  
  11. INTERFACE
  12. USES
  13.     {$U MemTypes.p    }    MemTypes,
  14.     {$U QuickDraw.p    }    QuickDraw,
  15.     {$U OSIntf.p        }    OSIntf,
  16.     {$U ToolIntf.p        }    ToolIntf,
  17.     {$U PopMenuIntf.p    }    PopMenuIntf;
  18.  
  19.  
  20. FUNCTION MyControl(varCode: INTEGER;
  21.                    theCntl: ControlHandle;
  22.                    message: INTEGER;
  23.                    param: LONGINT): LONGINT;
  24.     
  25.  
  26. IMPLEMENTATION
  27. CONST
  28.     VISIBLE        =    255;
  29.     INVISIBLE        =      0;
  30.     INACTIVE        =    255;
  31.     ACTIVE        =      0;
  32.     DRAW_ALL    =      0;
  33.     NOT_IN_CTL    =      0;
  34.     L_PIXELS        =     13;
  35.     GREY            =     16;
  36.     PARENT        =    $1B;
  37.  
  38.  
  39. TYPE
  40.     CtlDataRec    = record
  41.         popMenu:        MenuHandle;
  42.         menuProcID:    INTEGER;
  43.         hasColorQD:    Boolean;
  44.         markChar:        Char;
  45.         wFgColor:        RGBColor;
  46.         wBgColor:        RGBColor;
  47.         wContColor:    RGBColor;
  48.         mTitleColor:    RGBColor;
  49.         mBgColor:        RGBColor;
  50.         iNameColor:    RGBColor;
  51.         iKeyColor:        RGBColor;
  52.     end;
  53.     CtlDataPtr    =    ^CtlDataRec;
  54.     CtlDataHdl    =    ^CtlDataPtr;
  55.     
  56.     StateRec = record
  57.         savePort:    GrafPtr;
  58.         savePen:    PenState;
  59.         oldClip:        RgnHandle;
  60.         newClip:    RgnHandle;
  61.     end;
  62.     
  63.  
  64. (*****************************************************
  65. forward declarations
  66. *****************************************************)
  67.  
  68. PROCEDURE    doDrawCntl(theCntl: ControlHandle;
  69.                 vcLong, param: LONGINT);
  70.                 forward;
  71. FUNCTION        doTestCntl(theCntl: ControlHandle;
  72.                 param: LONGINT): LONGINT;
  73.                 forward;
  74. PROCEDURE    doCalcCRgns(theCntl: ControlHandle;
  75.                 param: LONGINT);
  76.                 forward;
  77. PROCEDURE    doInitCntl(theCntl: ControlHandle;
  78.                 vcLong: LONGINT);
  79.                 forward;
  80. PROCEDURE    doDispCntl(theCntl: ControlHandle;
  81.                 vcLong: LONGINT);
  82.                 forward;
  83. PROCEDURE    doAutoTrack(theCntl: ControlHandle;
  84.                 vcLong, param: LONGINT);
  85.                 forward;
  86.     
  87.  
  88. (*****************************************************
  89. MyControl: Main entry point.  Call appropriate
  90.     message-handling routine.
  91. *****************************************************)
  92.  
  93. FUNCTION MyControl(varCode: INTEGER;
  94.             theCntl: ControlHandle;
  95.             message: INTEGER;
  96.             param: LONGINT): LONGINT;
  97. VAR
  98.     vcLong:        LONGINT;
  99.  
  100. BEGIN
  101.     MyControl := 0;
  102.     vcLong := Ord4(varCode);
  103.     
  104.     CASE message OF
  105.         drawCntl:
  106.             doDrawCntl(theCntl, vcLong, param);
  107.         testCntl:
  108.             MyControl := doTestCntl(theCntl, param);
  109.         calcCRgns:
  110.             doCalcCRgns(theCntl, param);
  111.         initCntl:
  112.             doInitCntl(theCntl, vcLong);
  113.         dispCntl:
  114.             doDispCntl(theCntl, vcLong);
  115.         autoTrack:
  116.             doAutoTrack(theCntl, vcLong, param);
  117.     END;  { case }
  118. END;  { MyControl }
  119.  
  120.  
  121.  
  122. (*****************************************************
  123. CallMDEF: Calls the given ProcPtr, passing it the
  124.     given parameters.
  125. *****************************************************)
  126.  
  127. PROCEDURE CallMDEF(message: INTEGER;
  128.                 theMenu: MenuHandle;
  129.                 menuRect: Rect;
  130.                 hitPt: Point;
  131.                 whichItem: INTEGER;
  132.                 MDEFProc: ProcPtr);
  133. Inline 
  134.     $205F,     { move.l (sp)+, a0    ; get address of proc    }
  135.     $4E90;     { jsr (a0)            ; call the proc            }
  136.         
  137.     
  138.  
  139. (*****************************************************
  140. GetItemRect: Get the given item's rectangle.
  141. *****************************************************)
  142.  
  143. PROCEDURE GetItemRect(theCntl: ControlHandle;
  144.                             menuID: INTEGER;
  145.                             menuItem: INTEGER;
  146.                             VAR boxRect: Rect);
  147. VAR
  148.     hitPt:            Point;
  149.     menuHdl:        MenuHandle;
  150.     mDefProc:        Handle;
  151.     
  152. BEGIN
  153.     SetPt(hitPt, 0, 0);
  154.     menuHdl := GetMHandle(menuID);
  155.     mDefProc := menuHdl^^.menuProc;
  156.     LoadResource(mDefProc);
  157.     
  158.     CallMDEF(mItemRectMsg,
  159.             menuHdl,
  160.             boxRect,
  161.             hitPt,
  162.             menuItem,
  163.             ProcPtr(mDefProc^));
  164. END;  { GetItemRect }
  165.         
  166.     
  167.  
  168. (*****************************************************
  169. DrawMenuItem: Draw the given menu item in the
  170.     given rectangle.
  171. *****************************************************)
  172.  
  173. PROCEDURE DrawMenuItem(theCntl: ControlHandle;
  174.                             menuID: INTEGER;
  175.                             menuItem: INTEGER;
  176.                             boxRect: Rect);
  177. VAR
  178.     hitPt:            Point;
  179.     menuHdl:        MenuHandle;
  180.     mDefProc:        Handle;
  181.     
  182. BEGIN
  183.     SetPt(hitPt, 0, 0);
  184.     menuHdl := GetMHandle(menuID);
  185.     mDefProc := menuHdl^^.menuProc;
  186.     LoadResource(mDefProc);
  187.     
  188.     CallMDEF(mDrawItemMsg,
  189.             menuHdl,
  190.             boxRect,
  191.             hitPt,
  192.             menuItem,
  193.             ProcPtr(mDefProc^));
  194. END;  { DrawMenuItem }
  195.     
  196.         
  197.     
  198.  
  199. (*****************************************************
  200. GetContentColor: Get the window's content color.
  201. *****************************************************)
  202.  
  203. PROCEDURE GetContentColor(wPtr: WindowPtr;
  204.             VAR contColor: RGBColor);
  205. VAR
  206.     auxWinHdl:    AuxWinHndl;
  207.     winCTable:    WCTabHandle;
  208.     b_ignore:    Boolean;
  209.     i:            INTEGER;
  210.     
  211. BEGIN
  212.     b_ignore := GetAuxWin(wPtr, auxWinHdl);
  213.     winCTable := WCTabHandle(auxWinHdl^^.
  214.                                     awCTable);
  215.     
  216.     i := winCTable^^.ctSize;
  217.     
  218.     { search for wContentColor }
  219.     while ((i >= 0) and (winCTable^^.ctTable[i].value
  220.                         <> wContentColor)) do begin
  221.         i := i - 1;
  222.     end;
  223.     
  224.     { if we didn't find it, default to first entry }
  225.     if (i < 0) then
  226.         i := 0;
  227.     
  228.     contColor := winCTable^^.ctTable[i].rgb;
  229. END;  { GetContentColor }
  230.         
  231.     
  232.  
  233. (*****************************************************
  234. GetMenuColors: Initialize the control's menu color
  235.     information.  ctlData must be locked before calling
  236.     this routine.
  237. *****************************************************)
  238.  
  239. PROCEDURE GetMenuColors(theCntl: ControlHandle;
  240.             ctlData: CtlDataHdl);
  241. VAR
  242.     WhiteRGB:    RGBColor;
  243.     BlackRGB:    RGBColor;
  244.     mbarPtr:    MCEntryPtr;
  245.     titlePtr:        MCEntryPtr;
  246.     itemPtr:    MCEntryPtr;
  247.     
  248. BEGIN
  249.     { default colors }
  250.     WhiteRGB.red   := $FFFF;
  251.     WhiteRGB.green := $FFFF;
  252.     WhiteRGB.blue  := $FFFF;
  253.     BlackRGB.red   := 0;
  254.     BlackRGB.green := 0;
  255.     BlackRGB.blue  := 0;
  256.     
  257.     with theCntl^^ do begin
  258.         mbarPtr  := GetMCEntry(0, 0);
  259.         titlePtr := GetMCEntry(contrlMax, 0);
  260.         itemPtr  := GetMCEntry(contrlMax, contrlMin);
  261.     end;
  262.     
  263.     { get defaults from mbar, or default to B&W }
  264.     with ctlData^^ do begin
  265.         if (mbarPtr = NIL) then
  266.             begin
  267.                 if (titlePtr = NIL) then begin
  268.                     mTitleColor := BlackRGB;
  269.                     mBgColor    := WhiteRGB;
  270.                 end;
  271.             
  272.                 if (itemPtr = NIL) then begin
  273.                     iNameColor  := BlackRGB;
  274.                     iKeyColor   := BlackRGB;
  275.                 end;
  276.             end
  277.         else if (titlePtr = NIL) then begin
  278.             mTitleColor := mbarPtr^.mctRGB1;
  279.             mBgColor    := mbarPtr^.mctRGB2;
  280.             
  281.             if (itemPtr = NIL) then begin
  282.                 iNameColor  := mbarPtr^.mctRGB3;
  283.                 iKeyColor   := mbarPtr^.mctRGB3;
  284.             end;
  285.         end;
  286.         
  287.         { get colors and defaults from the title entry }
  288.         if (titlePtr <> NIL) then begin
  289.             mTitleColor := titlePtr^.mctRGB1;
  290.             mBgColor     := titlePtr^.mctRGB4;
  291.                     
  292.             if (itemPtr = NIL) then begin
  293.                 iNameColor := titlePtr^.mctRGB3;
  294.                 iKeyColor  := titlePtr^.mctRGB3;
  295.             end;
  296.         end;
  297.         
  298.         { set the item colors }
  299.         if (itemPtr <> NIL) then begin
  300.             iNameColor := itemPtr^.mctRGB2;
  301.             iKeyColor  := itemPtr^.mctRGB3;
  302.         end;
  303.     end;  { with ctlData^^ }
  304. END;  { GetMenuColors }
  305.         
  306.     
  307.  
  308. (*****************************************************
  309. InitColorInfo: Initialize the control's color information.
  310. *****************************************************)
  311.  
  312. PROCEDURE InitColorInfo(theCntl: ControlHandle;
  313.             ctlData: CtlDataHdl);
  314. VAR
  315.     i:            INTEGER;
  316.     wPtr:        WindowPtr;
  317.     
  318. BEGIN
  319.     HLock(Handle(ctlData));
  320.     
  321.     with ctlData^^ do begin
  322.         wPtr := theCntl^^.contrlOwner;
  323.         
  324.         { get the window's content color }
  325.         GetContentColor(wPtr, wContColor);
  326.         
  327.         { save the window's current fg and bg colors }
  328.         GetForeColor(wFgColor);
  329.         GetBackColor(wBgColor);
  330.         
  331.         { get the menu's and current item's colors }
  332.         GetMenuColors(theCntl, ctlData);
  333.     end;
  334.     
  335.     HUnlock(Handle(ctlData));
  336. END;  { InitColorInfo }
  337.  
  338.  
  339.  
  340.             
  341. (*****************************************************
  342. GetTitleRect: Get the title of the pop-up menu.
  343. *****************************************************)
  344.  
  345. PROCEDURE GetTitleRect(theCntl: ControlHandle;
  346.             VAR titleRect: Rect);
  347. VAR
  348.     fInfo:    FontInfo;
  349.     height:    INTEGER;
  350.     
  351. BEGIN
  352.     GetFontInfo(fInfo);
  353.     
  354.     with fInfo do begin
  355.         height := ascent + descent + leading;
  356.     end;
  357.     
  358.     { define the title's rect }
  359.     with theCntl^^ do begin
  360.         SetRect(titleRect, contrlRect.left,
  361.                            contrlRect.top,
  362.                            contrlRect.left +
  363.                                StringWidth(contrlTitle),
  364.                            contrlRect.top + height);
  365.         
  366.         with titleRect do begin
  367.             if (bottom > contrlRect.bottom - 1) then
  368.                 bottom := contrlRect.bottom - 1;
  369.             
  370.             if (right > contrlRect.right - 1) then
  371.                 right := contrlRect.right - 1;
  372.         end;  { with titleRect }
  373.     end;  { with theCntl^^ }
  374. END;  { GetTitleRect }
  375.  
  376.  
  377.  
  378.             
  379. (*****************************************************
  380. GetBoxRect: Get the box surrounding the pop-up
  381.     box.
  382. *****************************************************)
  383.  
  384. PROCEDURE GetBoxRect(theCntl: ControlHandle;
  385.             VAR boxRect: Rect);
  386. VAR
  387.     leftEdge:        INTEGER;
  388.     popMenu:        MenuHandle;
  389.     fInfo:            FontInfo;
  390.     height:            INTEGER;
  391.     menuProcID:    INTEGER;
  392.     ctlData:        CtlDataHdl;
  393.     
  394. BEGIN
  395.     ctlData := CtlDataHdl(theCntl^^.contrlData);
  396.     menuProcID := ctlData^^.menuProcID;
  397.     
  398.     if (menuProcID = textMenuProc) then
  399.         begin
  400.             GetFontInfo(fInfo);
  401.             
  402.             with fInfo do begin
  403.                 height := ascent + descent + leading;
  404.             end;
  405.             
  406.             with theCntl^^ do begin
  407.                 { find the left edge of the pop-up box }
  408.                 leftEdge := contrlRect.left +
  409.                             StringWidth(contrlTitle);
  410.                 
  411.                 popMenu := ctlData^^.popMenu;
  412.                 
  413.                 { defend against Menu Manager bug }
  414.                 CalcMenuSize(popMenu);
  415.                 
  416.                 { define the pop-up box's rect }
  417.                 SetRect(boxRect,
  418.                         leftEdge,
  419.                          contrlRect.top,
  420.                          leftEdge +
  421.                              popMenu^^.menuWidth +
  422.                             2,
  423.                         contrlRect.top + height + 1);
  424.             end;  { with theCntl^^ }
  425.         end  { menuProc = nil }
  426.     else begin
  427.         GetItemRect(theCntl,
  428.                     theCntl^^.contrlMax,
  429.                     theCntl^^.contrlMin,
  430.                     boxRect);
  431.     end;  { else }
  432.     
  433.     with theCntl^^ do begin
  434.         with boxRect do begin
  435.             if (bottom > contrlRect.bottom - 1) then
  436.                 bottom := contrlRect.bottom - 1;
  437.             
  438.             if (right > contrlRect.right - 1) then
  439.                 right := contrlRect.right - 1;
  440.         end;  { with boxRect }
  441.     end;  { with theCntl^^ }
  442. END;  { GetBoxRect }
  443.  
  444.  
  445.  
  446.             
  447. (*****************************************************
  448. GetCtlRect: Get the box surrounding the pop-up box
  449.     and its title.
  450. *****************************************************)
  451.  
  452. PROCEDURE GetCtlRect(theCntl: ControlHandle;
  453.             VAR ctlRect: Rect);
  454. VAR
  455.     boxRect:    Rect;
  456.     titleRect:    Rect;
  457.     
  458. BEGIN
  459.     GetBoxRect(theCntl, boxRect);
  460.     GetTitleRect(theCntl, titleRect);
  461.     
  462.     UnionRect(boxRect, titleRect, ctlRect);
  463.     
  464.     with ctlRect do begin
  465.         SetRect(ctlRect, left, top,
  466.             right + 1, bottom + 1);
  467.     end;
  468. END;  { GetCtlRect }
  469.  
  470.  
  471.  
  472.             
  473. (*****************************************************
  474. InstallMenus: Recursive routine to install a menu and
  475.     its sub-menus, if any.  It is only called once
  476.     (from doInitCntl()).
  477. *****************************************************)
  478.  
  479. PROCEDURE InstallMenus(rsrcID: INTEGER);
  480. VAR
  481.     mh:    MenuHandle;
  482.     ni:        INTEGER;
  483.     i:        INTEGER;
  484.     c:        Char;
  485.     
  486. BEGIN
  487.     mh := GetMenu(rsrcID);
  488.     InsertMenu(mh, -1);
  489.     ni := CountMItems(mh);
  490.     
  491.     { look for parent items }
  492.     for i := 1 to ni do begin
  493.         GetItemCmd(mh, i, c);
  494.         
  495.         { if it's a parent item, recurse on its child }
  496.         if (c = CHR(PARENT)) then begin
  497.             GetItemMark(mh, i, c);
  498.             InstallMenus(ORD(c));
  499.         end;
  500.     end;
  501. END;  { InstallMenus }
  502.  
  503.  
  504.  
  505.             
  506. (*****************************************************
  507. RemoveMenus: Recursive routine to remove a menu
  508.     and its sub-menus, if any.  It is only called once
  509.     (from doDispCntl()).
  510. *****************************************************)
  511.  
  512. PROCEDURE RemoveMenus(menuID: INTEGER);
  513. VAR
  514.     mh:    MenuHandle;
  515.     ni:        INTEGER;
  516.     i:        INTEGER;
  517.     c:        Char;
  518.     
  519. BEGIN
  520.     mh := GetMHandle(menuID);
  521.     ni := CountMItems(mh);
  522.     
  523.     { look for parent items }
  524.     for i := 1 to ni do begin
  525.         GetItemCmd(mh, i, c);
  526.         
  527.         { if it's a parent item, recurse on its child }
  528.         if (c = CHR(PARENT)) then begin
  529.             GetItemMark(mh, i, c);
  530.             RemoveMenus(ORD(c));
  531.         end;
  532.     end;
  533.     
  534.     { delete the menu from the menu list }
  535.     DeleteMenu(menuID);
  536.     ReleaseResource(Handle(mh));
  537. END;  { RemoveMenus }
  538.  
  539.  
  540.  
  541.             
  542. (*****************************************************
  543. ShrinkString: Make the given string fit in the given
  544.     box.  From a program by Bryan Stearns.
  545. *****************************************************)
  546.  
  547. PROCEDURE ShrinkString(VAR s: Str255; r: Rect);
  548. VAR
  549.     s_pix:    INTEGER;
  550.     s_len:    INTEGER;
  551.     room:    INTEGER;
  552.  
  553. BEGIN
  554.     { how much room do we have? }
  555.     room := (r.right - r.left) - L_PIXELS;
  556.     
  557.     { watch for weirdness }
  558.     if (room < 0) then begin
  559.         room := 0;
  560.         s[0] := CHR(0);
  561.     end;
  562.     
  563.     { get the width of the string }
  564.     s_pix := StringWidth(s);
  565.     
  566.     { will it fit? }
  567.     if (s_pix > room) then begin
  568.         s_len := LENGTH(s);
  569.         room := room - CharWidth('…');
  570.         
  571.         repeat
  572.             s_pix := s_pix - CharWidth(s[s_len]);
  573.             s_len := s_len - 1;
  574.         until ((s_pix < room) or (LENGTH(s) = 0));
  575.         
  576.         s_len := s_len + 1;
  577.         s[s_len] := '…';
  578.         s[0] := CHR(s_len);
  579.     end;
  580. END;  { ShrinkString }
  581.  
  582.  
  583.  
  584.             
  585. (*****************************************************
  586. DrawTitle: Draw the title of the pop-up menu control.
  587. *****************************************************)
  588.  
  589. PROCEDURE DrawTitle(theCntl: ControlHandle);
  590. VAR
  591.     titleRect:    Rect;
  592.     ctlData:    CtlDataHdl;
  593.     fInfo:        FontInfo;
  594.     baseline:    INTEGER;
  595.  
  596. BEGIN
  597.     with theCntl^^ do begin
  598.         ctlData := CtlDataHdl(contrlData);
  599.         
  600.         { if we need to draw in color, set the colors }
  601.         with ctlData^^ do begin
  602.             if (hasColorQD) then begin
  603.                 if (contrlHilite = titlePart) then
  604.                     begin
  605.                         RGBForeColor(wContColor);
  606.                         RGBBackColor(mTitleColor);
  607.                     end
  608.                 else begin
  609.                     RGBForeColor(mTitleColor);
  610.                     RGBBackColor(wContColor);
  611.                 end;
  612.             end;
  613.         end;
  614.         
  615.         { get the control's title box, and erase it }
  616.         GetTitleRect(theCntl, titleRect);
  617.         EraseRect(titleRect);
  618.             
  619.         { get info about the current font }
  620.         GetFontInfo(fInfo);
  621.         
  622.         { define baseline }
  623.         with fInfo do begin
  624.             baseline := contrlRect.top + ascent;
  625.         end;
  626.         
  627.         { move to baseline }
  628.         MoveTo(titleRect.left + 1, baseline);
  629.         
  630.         { draw control title (= the pop-up menu's title) }
  631.         DrawString(contrlTitle);
  632.         
  633.         { if we drew in color, restore the colors }
  634.         with ctlData^^ do begin
  635.             if (hasColorQD) then
  636.                 begin
  637.                     RGBForeColor(wFgColor);
  638.                     RGBBackColor(wBgColor);
  639.                 end
  640.             else if (contrlHilite = titlePart) then begin
  641.                 InvertRect(titleRect);
  642.             end;
  643.         end;
  644.     end;
  645. END;  { DrawTitle }
  646.  
  647.  
  648.  
  649.  
  650.             
  651. (*****************************************************
  652. DrawDropShadow: Draw the shadow around the
  653.     pop-up box of the pop-up menu control.
  654. *****************************************************)
  655.  
  656. PROCEDURE DrawDropShadow(
  657.                             theCntl: ControlHandle;
  658.                             boxRect: Rect);
  659. VAR
  660.     ctlData:    CtlDataHdl;
  661.  
  662. BEGIN
  663.     ctlData := CtlDataHdl(theCntl^^.contrlData);
  664.     
  665.     { if we need to draw in color, set the colors }
  666.     with ctlData^^ do begin
  667.         if (hasColorQD) then begin
  668.             RGBForeColor(mTitleColor);
  669.             RGBBackColor(mBgColor);
  670.         end;  { if }
  671.     end; { with ctlData^^ }
  672.     
  673.     with boxRect do begin
  674.         { draw the drop shadow }
  675.         MoveTo(right, top + 2);
  676.         LineTo(right, bottom);
  677.         LineTo(left + 2, bottom);
  678.     end;  { with boxRect }
  679.     
  680.     { if we drew in color, restore the colors }
  681.     with ctlData^^ do begin
  682.         if (hasColorQD) then begin
  683.             RGBForeColor(wFgColor);
  684.             RGBBackColor(wBgColor);
  685.         end;  { if }
  686.     end;  { with ctlData^^ }
  687. END;  { DrawDropShadow }
  688.  
  689.  
  690.  
  691.  
  692.             
  693. (*****************************************************
  694. DrawPopBox: Draw the pop-up box of the pop-up
  695.     menu control.  Also draws drop shadow.
  696. *****************************************************)
  697.  
  698. PROCEDURE DrawPopBox(theCntl: ControlHandle;
  699.                             vcLong: LONGINT);
  700. VAR
  701.     boxRect:        Rect;
  702.     itemStr:        Str255;
  703.     ctlData:        CtlDataHdl;
  704.     fInfo:            FontInfo;
  705.     baseline:        INTEGER;
  706.     menuProcID:    INTEGER;
  707.     
  708. BEGIN
  709.     ctlData := CtlDataHdl(theCntl^^.contrlData);
  710.     menuProcID := ctlData^^.menuProcID;
  711.     
  712.     if (menuProcID = textMenuProc) then
  713.         begin { standard textMenuProc }
  714.             with theCntl^^ do begin
  715.                 ctlData := CtlDataHdl(contrlData);
  716.                 GetBoxRect(theCntl, boxRect);
  717.                 
  718.                 { erase the box and shadow }
  719.                 with boxRect do begin
  720.                     SetPt(botRight, right + 2,
  721.                                     bottom + 2);
  722.                     EraseRect(boxRect);
  723.                     SetPt(botRight, right - 2,
  724.                                     bottom - 2);
  725.                 end;  { with }
  726.             
  727.                 { get current selection string }
  728.                 GetItem(GetMHandle(contrlMax),
  729.                                     contrlMin,
  730.                                     itemStr);
  731.                 
  732.                 { make the string fit in the boxRect }
  733.                 ShrinkString(itemStr, boxRect);
  734.                 
  735.                 { if color, set the colors }
  736.                 with ctlData^^ do begin
  737.                     if (hasColorQD) then begin
  738.                       RGBForeColor(mTitleColor);
  739.                       RGBBackColor(mBgColor);
  740.                     end;
  741.                 end;
  742.             
  743.                 { frame the box }
  744.                 FrameRect(boxRect);
  745.                 
  746.                 { get info about the current font }
  747.                 GetFontInfo(fInfo);
  748.                 
  749.                 { define baseline }
  750.                 with fInfo do begin
  751.                     baseline := contrlRect.top +
  752.                                     ascent;
  753.                 end;
  754.                 
  755.                 { if color, set the colors }
  756.                 with ctlData^^ do begin
  757.                     if (hasColorQD) then begin
  758.                       RGBForeColor(iNameColor);
  759.                     end;
  760.                 end;
  761.                     
  762.                     
  763.                 with boxRect do begin
  764.                     { draw the string in the popup box }
  765.                     MoveTo(left+L_PIXELS, baseline);
  766.                     DrawString(itemStr);
  767.                 end;  { with boxRect }
  768.                 
  769.                 { if color, restore the colors }
  770.                 with ctlData^^ do begin
  771.                     if (hasColorQD) then begin
  772.                         RGBForeColor(wFgColor);
  773.                         RGBBackColor(wBgColor);
  774.                     end;
  775.                 end;
  776.             end;  { with theCntl^^ }
  777.         end
  778.     else begin { non-standard menuProc }
  779.         GetBoxRect(theCntl, boxRect);
  780.         DrawMenuItem(theCntl,
  781.                     theCntl^^.contrlMax,
  782.                     theCntl^^.contrlMin,
  783.                     boxRect);
  784.     end;
  785.     
  786.     DrawDropShadow(theCntl, boxRect);
  787. END;  { DrawPopBox }
  788.  
  789.  
  790.             
  791. (*****************************************************
  792. DrawDisabled: Invert the pop-up menu control's title.
  793. *****************************************************)
  794.  
  795. PROCEDURE DrawDisabled(theCntl: ControlHandle);
  796. VAR
  797.     greyPat:    PatHandle;
  798.     ctlRect:    Rect;
  799.     
  800. BEGIN
  801.     { get the grey pattern from the System file }
  802.     greyPat:=PatHandle(GetResource('PAT ',GREY));
  803.     PenPat(greyPat^^);
  804.     ReleaseResource(Handle(greyPat));
  805.     
  806.     { set the pen mode }
  807.     PenMode(patBic);
  808.     
  809.     GetCtlRect(theCntl, ctlRect);
  810.     PaintRect(ctlRect);
  811. END;  { DrawDisabled }
  812.  
  813.  
  814.             
  815. (*****************************************************
  816. SaveState: Save the current drawing environment.
  817. *****************************************************)
  818.  
  819. PROCEDURE SaveState(theCntl: ControlHandle;
  820.             VAR theState: StateRec);
  821. VAR
  822.     ctlData:    CtlDataHdl;
  823.  
  824. BEGIN
  825.     { lock the control handle }
  826.     HLock(Handle(theCntl));
  827.     
  828.     with theCntl^^ do begin
  829.         with theState do begin
  830.             { save current grafPort; set to owner }
  831.             GetPort(savePort);
  832.             SetPort(contrlOwner);
  833.         
  834.             { allocate space for clipping regions }
  835.             oldClip := NewRgn;
  836.             newClip := NewRgn;
  837.             
  838.             { save old clipping region }
  839.             GetClip(oldClip);
  840.             
  841.             { set newClip region to given rectangle }
  842.             RectRgn(newClip, contrlRect);
  843.             
  844.             { newClip: intersection of rect and region }
  845.             SectRgn(oldClip, newClip, newClip);
  846.             
  847.             { set grafPorts' clip region to the result  }
  848.             SetClip(newClip);
  849.             
  850.             { save current pen state; normalize pen }
  851.             GetPenState(savePen);
  852.             PenNormal;
  853.             
  854.             { if we have color, get the menu color info }
  855.             ctlData := CtlDataHdl(contrlData);
  856.             if (ctlData^^.hasColorQD) then begin
  857.                 HLock(Handle(ctlData));
  858.                   GetMenuColors(theCntl, ctlData);
  859.                 HUnlock(Handle(ctlData));
  860.             end;
  861.         end;  { with theState }
  862.     end;  { with theCntl^^ }
  863.     
  864.     { unlock the control handle }
  865.     HUnlock(Handle(theCntl));
  866. END;  { SaveState }
  867.  
  868.  
  869.             
  870. (*****************************************************
  871. RestoreState: Restore the saved drawing environment.
  872. *****************************************************)
  873.  
  874. PROCEDURE RestoreState(theCntl: ControlHandle;
  875.             VAR theState: StateRec);
  876. BEGIN
  877.     with theState do begin
  878.         { restore saved states }
  879.         SetClip(oldClip);
  880.         SetPenState(savePen);
  881.         SetPort(savePort);
  882.         
  883.         { dispose of regions }
  884.         DisposeRgn(oldClip);
  885.         DisposeRgn(newClip);
  886.     end;  { with }
  887. END;  { RestoreState }
  888.  
  889.  
  890.             
  891. (*****************************************************
  892. doDrawCntl: Draw the pop-up menu box and title.
  893. *****************************************************)
  894.  
  895. PROCEDURE doDrawCntl(theCntl: ControlHandle;
  896.             vcLong, param: LONGINT);
  897. VAR
  898.     theState:    StateRec;
  899.  
  900. BEGIN
  901.     if (theCntl^^.contrlVis = VISIBLE) then begin
  902.         { save the current drawing environment }
  903.         SaveState(theCntl, theState);
  904.         
  905.         { lock the control }
  906.         HLock(Handle(theCntl));
  907.         
  908.         { draw the control }
  909.         DrawTitle(theCntl);
  910.         DrawPopBox(theCntl, vcLong);
  911.         
  912.         { if inactive, grey out the control }
  913.         if (theCntl^^.contrlHilite=INACTIVE) then begin
  914.             DrawDisabled(theCntl);
  915.         end;
  916.         
  917.         { unlock the control }
  918.         HUnlock(Handle(theCntl));
  919.             
  920.         { restore the saved drawing environment }
  921.         RestoreState(theCntl, theState);
  922.     end;  { if VISIBLE }
  923. END;  { doDrawCntl }
  924.  
  925.  
  926.  
  927.             
  928. (*****************************************************
  929. doTestCntl: Determine in which part of the control (if
  930.     any) the given point (in 'param') lies.    
  931. *****************************************************)
  932.  
  933. FUNCTION doTestCntl(theCntl: ControlHandle;
  934.             param: LONGINT): LONGINT;
  935. VAR
  936.     boxRect:    Rect;
  937.     
  938. BEGIN
  939.     if (theCntl^^.contrlHilite <> INACTIVE) then
  940.         begin  { control is active }
  941.             GetBoxRect(theCntl, boxRect);
  942.                 
  943.             if PtInRect(Point(param), boxRect) then
  944.                 doTestCntl := inPopUpBox
  945.             else
  946.                 doTestCntl := NOT_IN_CTL;
  947.         end
  948.     else  { control is inactive }
  949.         doTestCntl := NOT_IN_CTL;
  950. END;  { doTestCntl }
  951.  
  952.  
  953.  
  954.             
  955. (*****************************************************
  956. doCalcCRgns: Calculate the region the control
  957.     occupies in its window.
  958. *****************************************************)
  959.  
  960. PROCEDURE doCalcCRgns(theCntl: ControlHandle;
  961.             param: LONGINT);
  962. VAR
  963.     boxRect:    Rect;
  964.  
  965. BEGIN
  966.     if (BitAnd(param, $80000000) = $80000000) then
  967.         begin { wants indicator region - we have none }
  968.             param := BitAnd(param, $0FFFFFFF);
  969.             SetEmptyRgn(RgnHandle(param));
  970.         end
  971.     else begin
  972.         param := BitAnd(param, $0FFFFFFF);
  973.         
  974.         { set the given region to boxRect }
  975.         GetBoxRect(theCntl, boxRect);
  976.         RectRgn(RgnHandle(param), boxRect);
  977.     end;
  978. END;  { doCalcCRgns }
  979.  
  980.  
  981.  
  982.             
  983. (*****************************************************
  984. doInitCntl: Do any initialization required for the given
  985.     control.
  986. *****************************************************)
  987.  
  988. PROCEDURE doInitCntl(theCntl: ControlHandle;
  989.             vcLong: LONGINT);
  990. VAR
  991.     popMenu:        MenuHandle;
  992.     dfltMenu:        MenuHandle;
  993.     ctlRect:        Rect;
  994.     ctlData:        CtlDataHdl;
  995.     world:            SysEnvRec;
  996.     error:            OSErr;
  997.     markChar:        Char;
  998.     menuProcID:    INTEGER;
  999.     
  1000. BEGIN
  1001.     { lock the control record down  }
  1002.     HLock(Handle(theCntl));
  1003.  
  1004.     with theCntl^^ do begin
  1005.         { allocate a relocatable block }
  1006.         ctlData := CtlDataHdl(NewHandle(sizeof(
  1007.                                     CtlDataRec)));
  1008.             
  1009.         { is color QuickDraw running? }
  1010.         error := SysEnvirons(1, world);
  1011.         ctlData^^.hasColorQD := world.hasColorQD;
  1012.     
  1013.         { store a handle to the control data  }
  1014.         contrlData := Handle(ctlData);
  1015.         
  1016.         { erase the control's rectangle }
  1017.         EraseRect(contrlRect);
  1018.         
  1019.         { get a handle to the 'MENU' resource }
  1020.         popMenu := MenuHandle(
  1021.                         GetResource('MENU',
  1022.                                         contrlValue));
  1023.         
  1024.         { save the menuProc ID }
  1025.         ctlData^^.menuProcID := HiWord(
  1026.                         Ord4(popMenu^^.menuProc));
  1027.         
  1028.         { load pop-up menu, and its sub-menus }
  1029.         InstallMenus(contrlValue);
  1030.         popMenu := GetMHandle(contrlValue);
  1031.         
  1032.         { save the pop-up menu's menu handle }
  1033.         ctlData^^.popMenu := popMenu;
  1034.         
  1035.         { append resource names to the menu? }
  1036.         if ((BitAnd(vcLong, mRes) = mRes) and
  1037.                 (contrlRfCon <> 0)) then begin
  1038.             AddResMenu(popMenu,
  1039.                         OSType(contrlRfCon));
  1040.         end;
  1041.         
  1042.         { does the user want to use a check mark? }
  1043.         if (BitAnd(vcLong,mCheck)=mCheck) then begin
  1044.             { get a handle to the default menu }
  1045.             dfltMenu := GetMHandle(contrlMax);
  1046.             
  1047.             IF (CountMItems(dfltMenu) >= contrlMin) THEN
  1048.                 BEGIN
  1049.                 { get the default menu item's mark char }
  1050.                 GetItemMark(dfltMenu, contrlMin, markChar);
  1051.                 
  1052.                 { if no mark char, default to checkMark }
  1053.                 if (markChar = CHR(noMark)) then
  1054.                     BEGIN
  1055.                     markChar := CHR(checkMark);
  1056.                     
  1057.                     { set the default item's mark }
  1058.                     SetItemMark(dfltMenu, contrlMin, markChar);
  1059.                     END;
  1060.                 END
  1061.              ELSE BEGIN
  1062.                 markChar := CHR(checkMark);
  1063.             END;
  1064.             
  1065.             { save the mark character }
  1066.             ctlData^^.markChar := markChar;
  1067.         end;
  1068.             
  1069.         { if we have color, initialize the color info }
  1070.         if (world.hasColorQD) then begin
  1071.             InitColorInfo(theCntl, ctlData);
  1072.         end;
  1073.     
  1074.         { flag the default action proc }
  1075.         contrlAction := POINTER(-1);
  1076.     end;  { with theCntl }
  1077.         
  1078.     { unlock the control record before SetCTitle }
  1079.     HUnlock(Handle(theCntl));
  1080. END;  { doInitCntl }
  1081.  
  1082.  
  1083.  
  1084.             
  1085. (*****************************************************
  1086. doDispCntl: Do any de-allocation required for the
  1087.     given control.
  1088. *****************************************************)
  1089.  
  1090. PROCEDURE doDispCntl(theCntl: ControlHandle;
  1091.             vcLong: LONGINT);
  1092. VAR
  1093.     popMenu:    MenuHandle;
  1094.     ctlData:    CtlDataHdl;
  1095.     
  1096. BEGIN
  1097.     ctlData := CtlDataHdl(theCntl^^.contrlData);
  1098.     popMenu := ctlData^^.popMenu;
  1099.     
  1100.     { remove the pop-up and its sub-menus }
  1101.     RemoveMenus(popMenu^^.menuID);
  1102. END;  { doDispCntl }
  1103.  
  1104.  
  1105.  
  1106.             
  1107. (*****************************************************
  1108. doAutoTrack: This is the default action procedure for
  1109.     all controls of this type.  TrackControl() will
  1110.     place the value inPopBox in contrlHilite
  1111.     before calling doAutoTrack, so the old
  1112.     value will be lost before we can save it here.
  1113. *****************************************************)
  1114.  
  1115. PROCEDURE doAutoTrack(theCntl: ControlHandle;
  1116.             vcLong, param: LONGINT);
  1117. VAR
  1118.     popMenu:        MenuHandle;
  1119.     menuResult:    LONGINT;
  1120.     menuID:        INTEGER;
  1121.     menuItem:        INTEGER;
  1122.     boxRect:        Rect;
  1123.     globalPt:        Point;
  1124.     default:        INTEGER;
  1125.     saveTable:        MCTableHandle;
  1126.     ctlData:        CtlDataHdl;
  1127.  
  1128. BEGIN
  1129.     { lock control handle before dereferencing }
  1130.     HLock(Handle(theCntl));
  1131.     
  1132.     with theCntl^^ do begin
  1133.         { set hiliting to titlePart }
  1134.         contrlHilite := titlePart;
  1135.         
  1136.         { invert the title rect }
  1137.         DrawTitle(theCntl);
  1138.         
  1139.         { get the pop-up box's rectangle }
  1140.         GetBoxRect(theCntl, boxRect);
  1141.         
  1142.         { get the topLeft point, and convert to global  }
  1143.         SetPt(globalPt, boxRect.left, boxRect.top);
  1144.         LocalToGlobal(globalPt);
  1145.         
  1146.         { get a handle to the pop-up menu }
  1147.         ctlData := CtlDataHdl(contrlData);
  1148.         popMenu := ctlData^^.popMenu;
  1149.             
  1150.         { determine the default item }
  1151.         if (contrlMax = popMenu^^.menuID) then
  1152.             default := contrlMin
  1153.         else
  1154.             default := 1;
  1155.  
  1156.         { let the Menu Manager do the hard stuff }
  1157.         with globalPt do begin
  1158.             menuResult := PopUpMenuSelect(
  1159.                             popMenu,
  1160.                             v, h + 1, default);
  1161.         end;
  1162.         
  1163.         { what menu was the selection made from? }
  1164.         menuID := HiWord(menuResult);
  1165.         menuItem := LoWord(menuResult);
  1166.         
  1167.         { was a menu selection made? }
  1168.         if ((menuID <> 0) and ((menuID <> contrlMax)
  1169.                 or (menuItem<>contrlMin))) then begin
  1170.             { check the current selection }
  1171.             if (BitAnd(vcLong, mCheck) =
  1172.                             mCheck) then begin
  1173.                 { unmark previous selection }
  1174.                 SetItemMark(
  1175.                     GetMHandle(contrlMax),
  1176.                     contrlMin,
  1177.                     CHR(noMark));
  1178.                 
  1179.                 { mark current selection }
  1180.                 SetItemMark(
  1181.                     GetMHandle(menuID),
  1182.                     menuItem,
  1183.                     ctlData^^.markChar);
  1184.             end;  { if mCheck }
  1185.             
  1186.             { update the MenuSelect() results }
  1187.             contrlMax := menuID;
  1188.             contrlMin := menuItem;
  1189.             
  1190.             { redraw the pop-up box }
  1191.             { DrawPopBox(theCntl, vcLong); }
  1192.         end;  {if selection made }
  1193.     end;  { with }
  1194.     
  1195.     { unlock control handle before returning }
  1196.     HUnlock(Handle(theCntl));
  1197. END;  { doAutoTrack }
  1198.  
  1199. END.  { PopMenuCDEF.p }